import { Graph, Node } from "@antv/x6";
import { v4 as guid } from "uuid";
import { CellType, RgvRoadOrientation } from "../enums";
import { FieldUpdatedEvent, RgvConfig, RgvRoadConfig } from "../models";
import { cornerRadio } from "./cornerRadio";
import { Logger } from "src/service/logger";

export class RgvNode extends Node {

  static ShapeName = "Rgv";

  static register() {
    Graph.registerNode(RgvNode.ShapeName, RgvNode, true);
  }

  static unregister() {
    Graph.unregisterNode(RgvNode.ShapeName);
  }

  static getMeta(cfg?: RgvConfig): Node.Metadata {
    const size = { width: cfg?.Width ?? 500, height: cfg?.Height ?? 80 };
    const position = { x: cfg?.X ?? 0, y: cfg?.Y ?? 0 };

    const config: RgvConfig = {
      Id: cfg?.Id ?? guid(),
      Name: cfg?.Name ?? "RGV",
      Width: size.width,
      Height: size.height,
      X: position.x,
      Y: position.y,
      Plc: cfg?.Plc ?? {
        Ip: "",
        Port: 102,
        Interval: 1000,
      },
      Fields: cfg?.Fields ?? [],
      Status: cfg?.Status ?? {
        Id: guid(),
        Name: "Status",
        Description: "RGV状态",
        FontSize: 12,
        Visible: true,
        Maps: []
      },
      Road: cfg?.Road ?? {
        Orientation: RgvRoadOrientation.L2R,
        OffsetMin: 0,
        OffsetMax: 100,
        RgvField: ""
      },
      Type: CellType.Rgv,
    };

    const meta: Node.Metadata = {
      shape: RgvNode.ShapeName,
      size: size,
      position: position,
      id: config.Id,
      label: config.Name,
      zIndex: 2,
      attrs: {
        title: {
          text: config.Name,
        }
      },
      data: {
        cfg: config
      }
    };

    return meta;
  }

  update(cfg: RgvConfig): void {
    this.attr("title/text", cfg.Name);
    this.attr("plc/text", `PLC[${cfg.Plc.Ip ?? "ip"}:${cfg.Plc.Port ?? "port"}]`);

    this._updateOrientation(cfg.Road);
    this._updateRgvPosition(cfg.Road);
    this.data.cfg = cfg;
  }

  private _updateRgvPosition(road: RgvRoadConfig) {
    const { Orientation, OffsetMin, OffsetMax } = road;
    const max = OffsetMax + 0.01;
    const range = max - OffsetMin;
    const col = Math.max(OffsetMin, Math.min(OffsetMin, max));

    let offset = (col - OffsetMin) / (range + 0.005);
    if (Orientation === RgvRoadOrientation.R2L || Orientation == RgvRoadOrientation.B2T) {
      offset = 1 - offset;
    }
    this.attr("rgv/refX", offset);
  }

  private _updateOrientation(road: RgvRoadConfig) {
    const { Orientation } = road;
    let angle = 0;

    switch (Orientation) {
      case RgvRoadOrientation.L2R:
      case RgvRoadOrientation.R2L:
        angle = 0;
        break;
      case RgvRoadOrientation.T2B:
      case RgvRoadOrientation.B2T:
        angle = 90;
        break;
      default:
        throw new Error('Method not implemented.');
    }

    this.angle(angle, { absolute: true });
    this.attr("status/rotate", -angle);
    // this.attr("tooltip/rotate", -angle);
  }

  fieldValueUpdated(e: FieldUpdatedEvent): void {
    console.log("fieldValueUpdated", e);
    const { cfg }: { cfg: RgvConfig; } = this.data;
    this.data.Values = e.Values;
    const statusVal = e.Values["Status"];
    const status = cfg.Status.Maps.find(a => a.Key == statusVal);
    const color = status?.Color ?? "red";
    const statusText = status?.Description ?? `${statusVal}`;

    this.attr("rgv/fill", color);
    this.attr("status/fill", color);
    this.attr("status/text", `状态   ${statusText}`);

    if (cfg.Road.RgvField && Object.prototype.hasOwnProperty.call(e.Values, cfg.Road.RgvField)) {
      const currentColumnVal: number = e.Values[cfg.Road.RgvField];
      this._setCurrentColumn(currentColumnVal);
    }

    const content = cfg.Fields.filter(a => a.Visible).map(f => `${f.Description ?? f.Name}  ${e.Values[f.Name] ?? ""}`)
      .reduce((r, str) => r + "\n" + str, "");
    this.attr("tooltip/text", content);
  }

  getConfig(): RgvConfig {
    const { x, y } = this.position();
    const { width, height } = this.size();
    const config: RgvConfig = {
      ...this.data.cfg,
      Id: this.id,
      X: x,
      Y: y,
      Width: width,
      Height: height,
      Type: CellType.Rgv,
    };
    return config;
  }

  _setCurrentColumn(column: number): void {
    const { cfg }: { cfg: RgvConfig; } = this.data;
    const { Plc } = cfg;
    const { Orientation, OffsetMin, OffsetMax } = cfg.Road;

    const max = OffsetMax + 0.01;
    const range = max - OffsetMin;

    const col = Math.max(OffsetMin, Math.min(column, max));

    let offset = (col - OffsetMin) / (range + 0.005);
    if (Orientation === RgvRoadOrientation.R2L || Orientation == RgvRoadOrientation.B2T) {
      offset = 1 - offset;
    }
    this.logger?.log(offset);

    this.transition("attrs/rgv/refX", offset, {
      duration: Plc.Interval,
      progress: () => {
        const val = this.attr<number>("rgv/refX");
        this.attr("tooltip/refX", val);
        this.attr("status/refX", val);
      },
      finish: () => {
      }
    });
  }

  logger?: Logger;
  setLogger(logger: Logger) {
    this.logger = logger;
  }

  bindPositionAndSizeChange() {
    this.on("change:position", ({ current }) => {
      const cfg = {
        ...this.data.cfg,
        X: current?.x,
        Y: current?.y
      };
      this.data.cfg = cfg;
    });
    this.on("change:size", ({ current }) => {
      const cfg = {
        ...this.data.cfg,
        Width: current?.width,
        Height: current?.height
      };
      this.data.cfg = cfg;
    });
  }
}


RgvNode.config({
  width: 100,
  height: 100,
  markup: [
    {
      tagName: 'rect',
      selector: 'axisA',
    },
    {
      tagName: 'rect',
      selector: 'axisB',
    },
    {
      tagName: "rect",
      selector: "rgv"
    },
    {
      tagName: "text",
      selector: "plc",
    },
    {
      tagName: "text",
      selector: "title"
    },
    {
      tagName: "text",
      selector: "tooltip",
    },
    {
      tagName: "text",
      selector: "status",
    },
  ],
  attrs: {
    body: {

    },
    axisA: {
      refWidth: '100%',
      y: -15,
      height: 5,
      refY: "50%",
      fill: "#ddd",
      filter: {
        name: 'dropShadow',
        args: {
          dx: 1,
          dy: 1,
          blur: 1,
        },
      },
    },
    axisB: {
      refWidth: '100%',
      y: 10,
      height: 5,
      refY: "50%",
      fill: "#ddd",
      filter: {
        name: 'dropShadow',
        args: {
          dx: 1,
          dy: 1,
          blur: 1,
        },
      },
    },
    rgv: {
      fill: "#ff0000",
      height: 40,
      width: 40,
      refX: 0,
      refY: "50%",
      x: -20,
      y: -20,
      ...cornerRadio,
      filter: {
        name: 'dropShadow',
        args: {
          dx: 2,
          dy: 2,
          blur: 3,
        },
      },
    },
    plc: {
      text: 'PLC',
      refX: '0%',
      refY: '50%',
      x: 0,
      y: 60,
      fill: 'rgba(255,255,255,0.25)',
      fontSize: 16,
      textAnchor: 'start',
      textVerticalAnchor: 'bottom',
      class: 'text-shadow-1',
    },
    title: {
      refX: '100%',
      refY: '50%',
      x: -10,
      y: 60,
      fill: 'rgba(255,255,255,0.25)',
      fontSize: 20,
      textAnchor: 'end',
      textVerticalAnchor: 'bottom',
      class: 'text-shadow-1',
    },
    status: {
      text: '状态',
      refX: 0,
      refY: "50%",
      x: -20,
      y: 40,
      fill: 'rgba(255,255,255,0.75)',
      fontSize: 12,
      textAnchor: 'start',
      textVerticalAnchor: 'bottom',
      class: 'text-shadow-1',
    },
    tooltip: {
      text: "",
      height: 20,
      refX: 0,
      refY: "50%",
      fill: "#ff0",
      x: -25,
      y: -25,
      lineHeight: '1.4em',
      letterSpacing: "0.1em",
      textAnchor: 'left',
      textVerticalAnchor: 'bottom',
      class: 'text-shadow-1 tooltip',
    },
  }
});